package univ.uchiha.esiag.gon.domaine.local.impl;

import java.util.Vector;

import javax.rmi.PortableRemoteObject;

import org.omg.CORBA.ORB;
import org.omg.CORBA.Object;
import org.omg.PortableServer.POA;
import org.omg.PortableServer.POAHelper;

import univ.uchiha.esiag.gon.accesser.DataReactor;
import univ.uchiha.esiag.gon.accesser.DataReader;
import univ.uchiha.esiag.gon.accesser.DataWriter;
import univ.uchiha.esiag.gon.broadcaster.BroadcasterLocal;
import univ.uchiha.esiag.gon.domaine.SPManager;
import univ.uchiha.esiag.gon.domaine.local.LocalDomaineManager;
import univ.uchiha.esiag.gon.domaine.remote.RemoteDomaineManager;
import univ.uchiha.esiag.gon.publisher.Publisher;
import univ.uchiha.esiag.gon.publisher.Semaphore;
import univ.uchiha.esiag.gon.subscriber.Subscriber;
import univ.uchiha.esiag.gon.subscriber.impl.SubscriberImpl;
import univ.uchiha.esiag.gon.subscriber.impl._SubscriberImpl_Tie;
import univ.uchiha.esiag.gon.topic.Topic;

public class LocalDomaineManagerImpl implements LocalDomaineManager{
	private static final long serialVersionUID = 1L;
	
	private final int maxUse = 5;
	private Vector<SPManager> subscribers;
	private RemoteDomaineManager rdm;
	private Vector<SubscriberImpl>  subscribersImpls;
	private Vector<Publisher> publishers;
	private static LocalDomaineManagerImpl impl;
	private Semaphore semaphore;
	private LocalDomaineManagerImpl() {
		semaphore = new Semaphore();
		subscribers = new Vector<SPManager>();
		subscribersImpls = new Vector<SubscriberImpl>();
		publishers = new Vector<Publisher>();
	}
	public static LocalDomaineManagerImpl getInstance(){
		if(impl == null)
			impl = new LocalDomaineManagerImpl();
		return impl;
	}
	public void setRemoteDomainManager(RemoteDomaineManager rdm) {
		semaphore.signal();
		this.rdm = rdm;
	}
	private void addTopicOne(Topic topic){
		SubscriberImpl sub = new SubscriberImpl();
		subscribersImpls.add(sub);
		DataReactor dataReactor = sub.newDataReactor();
		
		new DataReader(topic, dataReactor);
		try{
			ORB orb = BroadcasterLocal.getORB();
			
			_SubscriberImpl_Tie tie = new _SubscriberImpl_Tie();
			tie.setTarget(sub);
			POA rootPOA = POAHelper.narrow(orb.resolve_initial_references("RootPOA"));
			rootPOA.the_POAManager().activate();
			
			Object objRef = rootPOA.servant_to_reference(tie);
			String iorSub = orb.object_to_string(objRef);
			
			
			Publisher publisher = new Publisher(topic.getName(), rdm.getSubscriber(topic.getName(), iorSub));
			publishers.add(publisher);
			
			DataWriter dataWriter = new DataWriter(publisher);
			topic.setDataWriter(dataWriter);
			
			SPManager sm = new SPManager(sub, publisher, maxUse);
			sm.use();
			subscribers.add(sm);
		}catch(Exception e){
			System.out.println(e.getMessage());
		}
	}
	private void addTopicTwo(Topic topic){
		int min = maxUse;
		SPManager sm = null;
		for(int i = 0;i<subscribers.size();i++){
			if(subscribers.get(i).getCounter() < min){
				sm = subscribers.get(i);
				min = subscribers.get(i).getCounter();
			}
		}
		if(sm != null){
			SubscriberImpl sub = sm.getSubscriber();
			DataWriter dataWriter = new DataWriter(sm.getPublisher());
			topic.setDataWriter(dataWriter);
			new DataReader(topic, sub.newDataReactor());
			sm.use();
		}
		else{
			addTopicOne(topic);
		}
	}
	public void addTopic(Topic topic){
		if(rdm == null)
			semaphore.stop();
		if(subscribers.size() == 0){
			addTopicOne(topic);
		}else{
			addTopicTwo(topic);
		}
	}
	private void addSubscriber(String topicName, String subscriber){
		for(int i = 0;i<publishers.size();i++){
			if(publishers.get(i).getTopicName().equals(topicName)){
				ORB orb = BroadcasterLocal.getORB();
				Object objRef = orb.string_to_object(subscriber);
				Subscriber sub = (Subscriber) PortableRemoteObject.narrow(objRef, Subscriber.class);
				publishers.get(i).addSubcriber(sub);
			}
		}
	}
	public String getSubscriber(String topicName, String subscriber) {
		for(int i = 0;i<subscribersImpls.size();i++){
			if(subscribersImpls.get(i).hasTopic(topicName)){
				SubscriberImpl sub = subscribersImpls.get(i);
				_SubscriberImpl_Tie tie = new _SubscriberImpl_Tie();
				tie.setTarget(sub);
				ORB orb = BroadcasterLocal.getORB();
				try{
					POA rootPOA = POAHelper.narrow(orb.resolve_initial_references("RootPOA"));
					rootPOA.the_POAManager().activate();
					
					Object objRef = rootPOA.servant_to_reference(tie);
					String ior = orb.object_to_string(objRef);
					addSubscriber(topicName, subscriber);
					return ior;
				}catch(Exception e){
					e.printStackTrace();
				}
			}
		}
		return null;
	}
	public void Init(Topic[] topics){
		for(int i = 0;i<topics.length;i++){
			addTopic(topics[i]);
		}
		for(int i = 0;i<topics.length;i++){
			topics[i].bootstrappe();
		}
	}
	public static void main(String[] args) {
		new BroadcasterLocal(args, new LocalDomaineManagerImpl()).start();
	}
}
